﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework;
using lumo.display.object2D;

namespace lumo.display.light
{
    /// <summary>
    /// Light manager class.
    /// </summary>
    public class LightManager : IOriginable2D, IColorable
    {
        // Private light list
        private List<Light> LightList = new List<Light>();
        internal RenderTarget2D ShadowTarget;
        internal RenderTarget2D TempTarget;
        internal Effect LinearizeEffect;
        internal Effect DelinearizeEffect;
        internal LumoComponent ParentComponent;

        /// <summary>
        /// Constructor.
        /// </summary>
        /// <param name="Content"></param>
        public LightManager(LumoComponent lumoComponent, Effect Linearize, Effect Delinearize, int Width, int Height)
        {
            // Set the effects
            LinearizeEffect   = Linearize;
            DelinearizeEffect = Delinearize;
            ParentComponent   = lumoComponent;
            ShadowTarget      = new RenderTarget2D(lumoComponent.GraphicsDevice, Width, Height, false, SurfaceFormat.Color, DepthFormat.None);
            TempTarget        = new RenderTarget2D(lumoComponent.GraphicsDevice, Width, Height, false, SurfaceFormat.Color, DepthFormat.None);
        }

        /// <summary>
        /// Constructor.
        /// </summary>
        /// <param name="Content"></param>
        public LightManager(LumoComponent lumoComponent, string Linearize, string Delinearize, int Width, int Height)
            : this(lumoComponent, lumoComponent.Content.Load<Effect>(Linearize), lumoComponent.Content.Load<Effect>(Delinearize), Width, Height)
        {
        }

        /// <summary>
        /// Clear the lights
        /// </summary>
        public void ClearLights() { LightList.Clear(); }

        /// <summary>
        /// Add a light
        /// </summary>
        public Light CreateLight(int Radius = 128, float Scale = 1f) 
        {
            Light l = new Light(ParentComponent.GraphicsDevice, Radius, Scale);
            LightList.Add(l);
            return l;
        }

        /// <summary>
        /// Remove a light
        /// </summary>
        public void RemoveLight(Light Light) { LightList.Remove(Light); }

        /// <summary>
        /// Lights enumerator
        /// </summary>
        public IEnumerable<Light> Lights { get { return LightList; } }

        /// <summary>
        /// Process the lights
        /// </summary>
        /// <param name="ImpassableTexture"></param>
        /// <returns>The processed light texture</returns>
        public Texture2D Apply(Texture2D ImpassableTexture)
        {
            // Set variables.
            GraphicsDevice GraphicsDevice = ParentComponent.GraphicsDevice;
            SpriteBatch    SpriteBatch    = ParentComponent.SpriteBatch;
            Rectangle      ScreenRectangle = new Rectangle(0, 0, ImpassableTexture.Width, ImpassableTexture.Height);

            // Get visible lights.
            List<Light> VisibleLights = new List<Light>();
            foreach (Light light in LightList)
            {
                Vector2 RealPosition = light.Position - this.Origin;
                if (ScreenRectangle.Contains((int)RealPosition.X, (int)RealPosition.Y))
                    VisibleLights.Add(light);
            }

            // Apply the lights
            foreach (Light light in VisibleLights)
            {
                light.Apply(this, ImpassableTexture);
            }

            // Apply Each Light
            Matrix M = Matrix.Identity;
            M *= Matrix.CreateOrthographicOffCenter(ScreenRectangle.Left, ScreenRectangle.Right, ScreenRectangle.Bottom, ScreenRectangle.Top, -1000, 1000);
            DelinearizeEffect.Parameters["WorldViewProjectionMatrix"].SetValue(M);


            // Apply the effect
            GraphicsDevice.SetRenderTarget(ShadowTarget);
            GraphicsDevice.Clear(new Color(0, 0, 0, 0));
            SpriteBatch.Begin(SpriteSortMode.Deferred, BlendState.Additive, SamplerState.LinearClamp, DepthStencilState.None, null, DelinearizeEffect, Matrix.CreateScale((float)ShadowTarget.Width / ScreenRectangle.Width, (float)ShadowTarget.Height / ScreenRectangle.Height, 1f));
            foreach (Light light in VisibleLights)
            {
                Vector2 RealPosition = light.Position - this.Origin;
                SpriteBatch.Draw(light.ProcessedTarget, new Rectangle((int)RealPosition.X - light.Radius, (int)RealPosition.Y - light.Radius, 2 * light.Radius, 2 * light.Radius), light.Color);
            }
            SpriteBatch.End();

            GraphicsDevice.SetRenderTarget(null);
            return ShadowTarget;
        }

        private Vector2 _Origin = Vector2.Zero;
        public Vector2 Size   { get { return Vector2.Zero; } }
        public Vector2 Origin { get { return _Origin; } set { _Origin = value; } }
        public float OX { get { return _Origin.X; } set { _Origin.X = value; } }
        public float OY { get { return _Origin.Y; } set { _Origin.Y = value; } }


        private Color _Color = Color.Black;
        public Color Color { get { return _Color; } set { _Color = value; } }
        public byte ColorR { get { return _Color.R; } set { _Color.R = value; } }
        public byte ColorG { get { return _Color.G; } set { _Color.G = value; } }
        public byte ColorB { get { return _Color.B; } set { _Color.B = value; } }
        public byte ColorA { get { return _Color.A; } set { _Color.A = value; } }
    }
}
